/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2006
* Sleepycat Software. All rights reserved.
*
* $Id: BINDelta.java,v 1.1 2006/05/06 09:00:12 ckaestne Exp $
*/
package com.sleepycat.je.tree;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.dbi.DatabaseId;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.LogException;
import com.sleepycat.je.log.LogReadable;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.log.LoggableObject;
import com.sleepycat.je.utilint.DbLsn;
/**
* BINDelta contains the information needed to create a partial (delta) BIN log
* entry. It also knows how to combine a full BIN log entry and a delta to
* generate a new BIN.
*/
public class BINDelta implements LoggableObject, LogReadable {
private DatabaseId dbId; // owning db for this bin.
private long lastFullLsn; // location of last full version
private List deltas; // list of key/action changes
private LogEntryType logEntryType; // type of log entry to use
// when writing to the log.
/**
* Read a BIN and create the deltas.
*/
public BINDelta(BIN bin) {
lastFullLsn = bin.getLastFullVersion();
dbId = bin.getDatabaseId();
deltas = new ArrayList();
logEntryType = bin.getBINDeltaType();
/*
* Save every entry that has been modified since the last full version.
* Note that we must rely on the dirty bit, and we can't infer any
* dirtiness by comparing the last full version LSN and the child
* reference LSN. That's because the ChildReference LSN may be earlier
* than the full version LSN because of aborts.
*/
for (int i = 0; i < bin.getNEntries(); i++) {
if (bin.isDirty(i)) {
deltas.add(new DeltaInfo(bin.getKey(i),
bin.getLsn(i),
bin.getState(i)));
}
}
}
/**
* For instantiating from the log.
*/
public BINDelta() {
dbId = new DatabaseId();
lastFullLsn = DbLsn.NULL_LSN;
deltas = new ArrayList();
}
/**
* @return a count of deltas for this BIN.
*/
int getNumDeltas() {
return deltas.size();
}
/**
* @return the dbId for this BIN.
*/
public DatabaseId getDbId() {
return dbId;
}
/**
* @return the last full version of this BIN
*/
public long getLastFullLsn() {
return lastFullLsn;
}
/**
* Create a BIN by starting with the full version and applying the deltas.
*/
public BIN reconstituteBIN(EnvironmentImpl env)
throws DatabaseException {
/* Get the last full version of this BIN. */
BIN fullBIN = (BIN) env.getLogManager().get(lastFullLsn);
DatabaseImpl db = env.getDbMapTree().getDb(dbId);
/*
* In effect, call fullBIN.postFetchInit(db) here. But we don't want
* to do that since it will put fullBIN on the INList. Since this is
* either recovery or during the Cleaner run, we don't want it on the
* INList.
*/
fullBIN.setDatabase(db);
fullBIN.setLastFullLsn(lastFullLsn);
/* Process each delta. */
fullBIN.latch();
for (int i = 0; i < deltas.size(); i++) {
DeltaInfo info = (DeltaInfo) deltas.get(i);
/*
* The BINDelta holds the authoritative version of each entry. In
* all cases, its entry should supercede the entry in the full
* BIN. This is true even if the BIN Delta's entry is knownDeleted
* or if the full BIN's version is knownDeleted. Therefore we use
* the flavor of findEntry that will return a knownDeleted entry if
* the entry key matches (i.e. true, false) but still indicates
* exact matches with the return index. findEntry only returns
* deleted entries if third arg is false, but we still need to know
* if it's an exact match or not so indicateExact is true.
*/
int foundIndex = fullBIN.findEntry(info.getKey(), true, false);
if (foundIndex >= 0 &&
(foundIndex & IN.EXACT_MATCH) != 0) {
foundIndex &= ~IN.EXACT_MATCH;
/*
* The entry exists in the full version, update it with the
* delta info.
*/
if (info.isKnownDeleted()) {
fullBIN.setKnownDeleted(foundIndex);
} else {
fullBIN.updateEntry
(foundIndex, info.getLsn(), info.getState());
}
} else {
/* The entry doesn't exist, add a new entry from the delta. */
if (!info.isKnownDeleted()) {
ChildReference entry =
new ChildReference(null,
info.getKey(),
info.getLsn(),
info.getState());
boolean insertOk = fullBIN.insertEntry(entry);
assert insertOk;
}
}
}
/*
* Reset the generation to 0, all this manipulation might have driven
* it up.
*/
fullBIN.setGeneration(0);
fullBIN.releaseLatch();
return fullBIN;
}
/*
* Logging support
*/
/*
* @see com.sleepycat.je.log.LoggableObject#getLogType()
*/
public LogEntryType getLogType() {
return logEntryType;
}
/**
* @see LoggableObject#marshallOutsideWriteLatch
* Can be marshalled outside the log write latch.
*/
public boolean marshallOutsideWriteLatch() {
return true;
}
/**
* @see LoggableObject#countAsObsoleteWhenLogged
*/
public boolean countAsObsoleteWhenLogged() {
return false;
}
/*
* Nothing to do after the act of logging this entry.
* @see com.sleepycat.je.log.LoggableObject#postLogWork(
* com.sleepycat.je.util.DbLsn)
*/
public void postLogWork(long justLoggedLsn) {
}
/*
* @see com.sleepycat.je.log.LogReadable#readFromLog(
* java.nio.ByteBuffer)
*/
public void readFromLog(ByteBuffer itemBuffer,byte entryTypeVersion)
throws LogException {
dbId.readFromLog(itemBuffer, entryTypeVersion); // database id
lastFullLsn = LogUtils.readLong(itemBuffer); // last version
int numDeltas = LogUtils.readInt(itemBuffer);
for (int i=0; i < numDeltas; i++) { // deltas
DeltaInfo info = new DeltaInfo();
info.readFromLog(itemBuffer, entryTypeVersion);
deltas.add(info);
}
}
/*
* @see com.sleepycat.je.log.LoggableObject#getLogSize()
*/
public int getLogSize() {
int size =
dbId.getLogSize() + // database id
LogUtils.LONG_BYTES + // last version
LogUtils.INT_BYTES; // num deltas
for (int i = 0; i < deltas.size(); i++) { // deltas
DeltaInfo info = (DeltaInfo) deltas.get(i);
size += info.getLogSize();
}
return size;
}
/*
* @see com.sleepycat.je.log.LoggableObject#writeToLog
* (java.nio.ByteBuffer)
*/
public void writeToLog(ByteBuffer logBuffer) {
dbId.writeToLog(logBuffer); // database id
LogUtils.writeLong(logBuffer, lastFullLsn); // last version
LogUtils.writeInt(logBuffer, deltas.size()); // num deltas
for (int i = 0; i < deltas.size(); i++) { // deltas
DeltaInfo info = (DeltaInfo) deltas.get(i);
info.writeToLog(logBuffer);
}
}
/*
* @see LogReadable#dumpLog(java.lang.StringBuffer)
*/
public void dumpLog(StringBuffer sb, boolean verbose) {
dbId.dumpLog(sb, verbose);
sb.append("<lastFullLsn>");
sb.append(DbLsn.toString(lastFullLsn));
sb.append("</lastFullLsn>");
sb.append("<deltas size=\"").append(deltas.size()).append("\"/>");
for (int i = 0; i < deltas.size(); i++) { // deltas
DeltaInfo info = (DeltaInfo) deltas.get(i);
info.dumpLog(sb, verbose);
}
}
/**
* @see LogReadable#logEntryIsTransactional
*/
public boolean logEntryIsTransactional() {
return false;
}
/**
* @see LogReadable#getTransactionId
*/
public long getTransactionId() {
return 0;
}
}